<?php

/**
 * Albireo Kernel
 *
 * @copyright  Copyright (c) 2010 Albireo Solutions
 * @package    Kernel
 */

namespace KernelModule\UsersModule;

use Nette\Application\AppForm;
use Nette\Forms\Form;

use \dibi;

use Albireo\Forms\AlbireoForm;

use \Celebrio\AccessControl\AlbireoUser;
use \Celebrio\AccessControl\UserManagement;
use \Celebrio\Core\ServiceProvider;
use \Celebrio\Modules;

use KernelModule\AclModule\AclManagement;

/**
 * Presenter for user management
 *
 * @inherits cancelClicked() from Kernel\Acl\BasePresenter
 * @inherits createComponentPaginator() from Kernel\Acl\BasePresenter
 * @inherits addFormButtons from Kernel\Acl\BasePresenter
 * @inherits and overrides createComponentDelete() from Kernel\Acl\BasePresenter
 *
 * @extends \Kernel\Acl\BasePresenter
 * @author Pavel
 */
class UsersPresenter extends \KernelModule\AclModule\BasePresenter {

    /**
     * This array should be const/final, but PHP sux in this. Just make sure
     * you don't change the values (do it only here, in the declaration).
     *
     * @var array (index => string_language_abbr)
     */
    private static $languages = array("en_US", "cs_CZ", "sk_SK");
    
    /** @var Celebrio\AccessControl\UserManagement */
    private $userManager;

    /** @var Kernel\UserManagment */
    private $rolesModel;

    /** @var KernelModule\AclModule\AclManagment */
    private $aclManager;

    /**
     * @return \Celebrio\AccessControl\UserManagement
     */
    private function getUserManager() {
        if ($this->userManager === NULL) {
            $this->userManager = new UserManagement();
        }
        return $this->userManager;
    }

    /**
     * @return AclManagement
     */
    private function getAclManager() {
        if ($this->aclManager === NULL) {
            $this->aclManager = new AclManagement();
        }
        return $this->aclManager;
    }

    /**
     * Lazy getter for RolesModel
     * @return use RolesModel
     */
    private function getRolesModel() {
        if ($this->rolesModel === NULL) {
            $this->rolesModel = new RolesModel();
        }
        return $this->rolesModel;
    }
    
    public function actionRegister() {
        if ($this->getUser()->isLoggedIn()) {
            $this->redirect("Overview");
        }
        $this->getTemplate()->title = _("Registration of new user");
    }

    public function actionHooks($userId) {
        
//        $hooks = $this->getSession("hooks");
//        if ($hooks->data === null) {
//            // first time here, let's initialize hooks
////            $people = ServiceProvider::getService(Modules::PEOPLE);
//
//            $hooks->data = UserManagement::$creation_hooks;
//            \Nette\Debug::dump($hooks->data);
//            throw new \Exception("created the session");
//        }
//
//        if ($hooks->data === array()) {
//            // everything processed
//            $hooks->remove();
//            throw new \Exception("processed, removed");
//        } else {
//            // do processing
//            foreach($hooks->data as $key => $hook) {
//                if (isset($hook["contact_service"])) {
//                    $cs = ServiceProvider::getAllServices(Modules::CONTACTS);
//                    \Nette\Debug::dump($cs);
////                    $hook_obj = $cs
//                    unset($hooks->data[$key]);
//                    throw new \Exception("service");
//                } elseif (isset($hook["class"])) {
//                    $hook_obj = new $hook["class"];
//                    \Nette\Debug::dump($hook_obj);
//                    unset($hooks->data[$key]);
//                    throw new \Exception("class");
//                } else {
//                    throw new \InvalidArgumentException("Incorrect hook format.");
//                }
//
//                throw new \Exception("hook");
//
//                if ($hook["requires"] === array()) {
//                    \Nette\Debug::dump($hook);
//                    // process hook
//                    foreach($hooks->data as &$other) {
//                        $other["requires"] = array_diff($other["requires"], $hook["provides"]); // remove provided from other hook needs
//                    }
//
//                }
//            }
//            throw new \Exception("pop");
//        }
//        throw new \Exception;
//        $hooks = UserManagement::$creation_hooks;
//        $user = $this->getUserManager()->getUser($userId);
//        foreach ($hooks as $hook) {
//            ServiceProvider::getService($hook["service"])->$hook["method"]($user);
//        }
        $this->redirect("default");
    }
    
    public function renderDefault($search) {
        $this->search = $search;
        
        $this->template->title = _("Users List");
        $this->template->search = $search;

        $this->bringUsers($search);
    }

    public function handleDefault($search) {
        $this->bringUsers($search);
        $this->invalidateControl("users");
    }
    
    public function actionAdd() {
        $this->template->title = _("Add New User");
    }

    public function actionEdit($userName) {
        $this->template->title = _("Edit User");
        $this->template->userName = $userName;

        $user = $this->getUserManager()->getUserbyUsername($userName);
        $roles = $this->getUserManager()->getRolesByUserId($user->getId());

        $rolesId = array();
        foreach($roles as $role) {
            $rolesId[] = $role->getId();
        }

        $default = array(
            "id" => $user->getId(),
            "user_name" => $user->getUsername(),
            "first_name" => $user->getFirstName(),
            "last_name" => $user->getLastName(),
            "email" => $user->getEmail(),
            "language" => array_search($user->getLanguage(), self::$languages), //sets the index of the current language
            "timezone" => $user->getTimezone(),
            "roles" => $rolesId,
        );
        
        $form = $this->getComponent('addEdit');
        $form->addHidden('user_name');
        $form->setDefaults($default);
    }

    private function formAddEdit() {
        // TODO: this password setting is against ISO/IEC 27001
        $form = new AlbireoForm();
        if ($this->getAction()=='add' || $this->getAction() == "register") {
            $form->addText('user_name', _('Name:'), 30)
                ->addRule(Form::FILLED, _("Please, enter the username"));
            $form->addPassword('password', _('Password:'), 30)
                ->addRule(Form::FILLED, _('You have to fill password.'));
            $form->addPassword('password2', _('Password again:'), 30)
                ->addRule(Form::FILLED, _('Reenter your password.'))
                ->addRule(Form::EQUAL, _('Passwords do not match.'), $form['password']);
        }
        $form->addText('first_name', _('First Name:'), 30);
        $form->addText('last_name', _('Last Name:'), 30);
        $form->addText('email', _('E-mail:'), 30)
                ->setDefaultValue("@")
                ->addRule(Form::FILLED, _("Please, enter your e-mail"))
                ->addRule(Form::EMAIL, _("Invalid e-mail format"));
        $form->addSelect("language", _("Language:"), self::$languages);
        
        if ($this->getAction() !== "register") {
            $form->addMultiSelect('roles', _('Roles:'), $this->getRolesModel()->getTreeValues(), 10);
        }
        $form->addText("timezone", _("Timezone:"), 10)
                ->setDefaultValue("GMT+1")
                ->addRule(Form::FILLED, _("Please, fill the timezone"));
        return $form;
    }
    
    protected function createComponentRegister() {
        $form = $this->formAddEdit();
        $form->addSubmit('register', _('Register'));
        
        $form->onSubmit[] = array($this, 'registrationSubmitted');
        
        return $form;
    }
    
    public function registrationSubmitted(AlbireoForm $form) {
        $values = $form->getValues();
        $newUser = $this->userFromValues($values);
        
        if ($this->getUserManager()->addUser($newUser, $values["password"])) {
            $this->flashMessage(_('Registration succesful.'), 'ok');
            $this->action("hooks");
        } else {
            $this->flashMessage(_("An error occured in the registration process."), 'error');
        }
    }
    
    protected function createComponentAddEdit() {
        $form = $this->formAddEdit();
        $this->addFormButtons($form);
        
        return $form;
    }
    
    private function userFromValues($values) {
        $newUser = new AlbireoUser();
        $newUser->setUsername($values["user_name"]);
        $newUser->setFirstName($values["first_name"]);
        $newUser->setLastName($values["last_name"]);
        $newUser->setEmail($values["email"]);
        $newUser->setLanguage(self::$languages[$values["language"]]);
        $newUser->setTimezone($values["timezone"]);
        
        return $newUser;
    }

    public function addOrEditSubmitted(AlbireoForm $form) {
        $this->search = ""; //clearing the filter, so all new (and old) users will be displayed
        $values = $form->getValues();

        $newUser = $this->userFromValues($values);

        if ($this->getAction() == "add") {
            if ($this->getUserManager()->addUser($newUser, $values["password"])) {
                $this->flashMessage(_('The user has been added.'), 'ok');
                $this->getUserManager()->addRolesforUser($values["roles"], $newUser->getId());
                $this->redirect("hooks", $newUser->getId());
            } else {
                $this->flashMessage(_("The user can't be added, maybe already exists."), 'error');
            }
        } elseif($this->getAction() == "edit") {
            $newUser->setId($values["id"]);
            if ($this->getUserManager()->updateUser($newUser)) {
                $this->flashMessage(_('The user has been edited.'), 'ok');
                $this->getUserManager()->deleteAllRolesforUser($newUser->getId());
                $this->getUserManager()->addRolesforUser($values["roles"], $newUser->getId());
                $this->redirect("default");
            } else {
                $this->flashMessage(_("The user info can't be updated."), 'error');
            }
        } else throw new \InvalidStateException(); //this should never happen
    }

    // -- Delete user -----------------------------
    
    public function actionDelete($userName) {
        $deletedUser = $this->getUserManager()->getUserByUsername($userName);
        if ($deletedUser == NULL) {
            $this->flashMessage(_("The user doesn't exist."), "error");
            $this->redirect('Users:');
        }
        $this->template->title = _("Delete User");
        $this->template->user_name = $deletedUser->getUsername();
        $this["delete"]->setDefaults(array("user_name" => $deletedUser->getUsername()));
    }

    protected function createComponentDelete() {
        $form = parent::createComponentDelete();
        $form->addHidden("user_name");
        return $form;
    }
    
    public function deleteOnFormSubmitted(AppForm $form){
        if ($form['delete']->isSubmittedBy()) {
            $values = $form->getValues();
            $deletedUser = $this->getUserManager()->getUserByUsername($values["user_name"]);
            if ($this->getUserManager()->deleteUser($deletedUser)) {
                $this->flashMessage(_('The user has been deleted succesfuly.'), 'ok');
            } else $this->flashMessage(_("The user has not been deleted. Check the DB rights and dependencies."), "error");
            $this->redirect("default");
        }
        else
            $this->redirect('Users:');
    }


    // -- Access for the user -----------------------------
    
    public function actionAccess($userName) {
        $this->template->title = _("Authorized Access");

        $user = $this->getUserManager()->getUserByUsername($userName);
        $roles = $this->getUserManager()->getRolesByUserId($user->getId());
        $access = array();
        foreach($roles as $role) {
            $access[$role->getName()] = $this->getAclManager()->getAccessForTheRole($role->getId());
        }
        
        $this->template->user_name = $user->getUsername();
        $this->template->access = $access;
    }


    protected function createComponentSearch($name) {
        $form = new AlbireoForm($this, $name);
        $form->addText('user', _('Search for user:'), 30);
        $form->addSubmit('search', _('Search'));
        $form->onSubmit[] = array($this, 'searchSubmitted');
        return $form;
    }
    
    public function searchSubmitted(AppForm $form) {
        $values = $form->getValues();
        $this->search = $values["user"];
        $this->redirect("default");
    }

    
    

    private function bringUsers($search) {
        $form = $this->getComponent('search');
        $form->setDefaults(array("user" => $this->search));

        $paginator = $this->getComponent("paginator")->getPaginator();

        try {
            $usersDataSource = $this->getUserManager()->getFilteredUsersDataSource($this->search);

            $paginator->itemCount = $usersDataSource->count();
            $usersDataSource->applyLimit($paginator->itemsPerPage, $paginator->offset);

            $users_roles = array();
            foreach($usersDataSource as $user) {
                $users_roles[$user["user_name"]] = $this->getUserManager()->getRolesByUserId($user["id"]);
            }
            $this->template->users_roles = $users_roles;
            $this->template->users = $usersDataSource;

        } catch (\KernelModule\UsersModule\UserSearchingException $e) {
            $this->template->users = array();
        }
    }
}
